/*
 * @(#)JaxpXmlDataHandler.java $version 2012-3-2
 * 
 * Copyright 2011 RogueDevelopment Gruop. All rights Reserved.
 * ROGUEDEVELOPMENT PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */
package com.nadostar.extensions.data;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.nadostar.extensions.util.FileResource;
import com.nadostar.extensions.util.Resource;
import com.nadostar.extensions.util.ResourceLoader;
import com.nadostar.extensions.util.XMLDOMUtil;

/**
 * @author LEESUNG
 * 
 */
public class JaxpXmlDataHandler extends AbstractDataHandler {
	private Node element;

	JaxpXmlDataHandler(String location) {
		super(location);
		initialize();
	}

	JaxpXmlDataHandler(Node element) {
		this.element = element;
	}

	public void initialize() {
		ResourceLoader loader = new ResourceLoader();
		Resource resource = loader.loadResource(location);

		if (resource == null) {
			throw new RuntimeException();
		}

		Element localElement = parseResource(resource);
		includeSubConfig(loader, localElement, localElement);

		if (resource instanceof FileResource) {
			((FileResource) resource).sync();
		}

		this.element = localElement.cloneNode(true);
		this.resource = resource;
	}

	public void destroy() {
		if (element.getOwnerDocument() == null) {
			XMLDOMUtil.destroyDocument((Document) element);
		}
	}

	private Element parseResource(Resource resource) {
		try {
			InputStream inputStream = resource.openStream();
			try {
				return XMLDOMUtil.loading(inputStream).getDocumentElement();
			} finally {
				inputStream.close();
			}
		} catch (Exception e) {
			throw new DataHandlerException(e);
		}
	}

	private void includeSubConfig(ResourceLoader loader, Element rootElement,
			Element element) {
		NodeList includes = ((Element) element.cloneNode(true))
				.getElementsByTagName("include");

		for (int idx = 0; idx < includes.getLength(); idx++) {
			Element including = (Element) includes.item(idx);
			String includeFile = including.getFirstChild().getNodeValue();

			Resource resource = loader.loadResource(includeFile);

			if (resource == null) {
				throw new RuntimeException();
			}

			Element included = parseResource(resource);

			NodeList childNodes = included.getChildNodes();

			for (int subIdx = 0; subIdx < childNodes.getLength(); subIdx++) {
				Node orgChildNode = childNodes.item(subIdx);
				Node newChildNode = rootElement.getOwnerDocument().importNode(
						orgChildNode, true);
				rootElement.appendChild(newChildNode);
			}

			log.info("include config " + includeFile);

			includeSubConfig(loader, rootElement, included);
		}
	}

	public String evaluate(String key, boolean resolve) {
		Node selected = XMLDOMUtil.getNode(element, key);

		if (selected != null) {
			String value = XMLDOMUtil.evaluate(selected);
			return resolve ? resolve(value) : value;
		}

		return null;
	}

	public boolean containsKey(String key) {
		Node selected = XMLDOMUtil.getNode(element, key);
		return selected != null;
	}

	public List<String> getValues(String key) {
		NodeList elements = XMLDOMUtil.getNodes(element, key);

		if (elements == null) {
			return Collections.emptyList();
		}

		List<String> values = new ArrayList<String>(elements.getLength());

		for (int idx = 0; idx < elements.getLength(); idx++) {
			values.add(XMLDOMUtil.evaluate(elements.item(idx)));
		}

		return values;
	}

	public DataHandler peek(String path) {
		Node subConfigElement = XMLDOMUtil.getNode(element, path);

		if (subConfigElement == null) {
			return null;
		}

		return new JaxpXmlDataHandler(subConfigElement);
	}

	public DataHandler select(String path) throws DataHandlerException {
		DataHandler handler = peek(path);

		if (handler == null) {
			throw new DataHandlerException(path + " is not found in "
					+ identity());
		}

		return handler;
	}

	public List<DataHandler> selectAll(String path) throws DataHandlerException {
		List<DataHandler> subConfigs = peekAll(path);

		if (subConfigs.isEmpty()) {
			throw new DataHandlerException(path + " is not found in "
					+ identity());
		}

		return subConfigs;
	}

	public List<DataHandler> peekAll(String path) {
		NodeList nodeList = XMLDOMUtil.getNodes(element, path);

		if (nodeList == null) {
			return Collections.emptyList();
		}

		List<DataHandler> subConfigs = new ArrayList<DataHandler>(
				nodeList.getLength());

		for (int idx = 0; idx < nodeList.getLength(); idx++) {
			subConfigs.add(new JaxpXmlDataHandler(nodeList.item(idx)));
		}

		return subConfigs;
	}

	public List<DataHandler> peekChildren() {
		NodeList nodeList = element.getChildNodes();
		List<DataHandler> subConfigs = new ArrayList<DataHandler>(
				nodeList.getLength());

		for (int idx = 0; idx < nodeList.getLength(); idx++) {
			Node node = nodeList.item(idx);

			if (node.getNodeType() == Node.ELEMENT_NODE) {
				subConfigs.add(new JaxpXmlDataHandler(node));
			}
		}

		return subConfigs;
	}

	public String getRootName() {
		return element.getNodeName();
	}
}
